Introduction
The data utilized in this analysis is from a dataset called
loan_default from Applied Analytics through Case
Studies Using SAS and R, by Deepti Gupta, published by APress, ISBN
- 978-1-4842-3525-6.
Purpose of Data
Collection
The data for loan_default was collected to generate a
model for predicting which customers are most likely to default.
Description of the
Data Collection or Generation Process
The exact methods used to collect the data in
loan_default are unfortunately unknown.
Sample Size and
Number of Feature Variables
loan_default has a total sample size of 1,000, contains
15 feature variables, and has 1 label.
Itemized List of
Feature Variables
The feature variables of loan_default are as
follows:
- Variable 1:
Checking_Amount (Numeric)
- Variable 2:
Term (displayed in months (Numeric))
- Variable 3:
Credit_score (Numeric)
- Variable 4:
Gender (Categorical)
- Variable 5:
Marital_status (Categorical)
- Variable 6:
Car_loan (1- Own car loan, 0- Does not own
car loan – Numeric)
- Variable 7:
Personal_loan (1- Own Personal loan, 0-
Does not own Personal loan – Numeric)
- Variable 8:
Home_loan (1- Own Home loan, 0- Does not
own Home loan – Numeric)
- Variable 9:
Education_loan (1- Own Education loan, 0-
Does not own Education loan – Numeric)
- Variable 10:
Emp_status (Categorical)
- Variable 11:
Amount (Numeric)
- Variable 12:
Saving_amoun (Numeric)
- Variable 13:
Emp_duration (which is displayed in months
(Numeric))
- Variable 14:
Age (which is displayed in years
(Numeric))
- Variable 15:
No_of_credit_account (Numeric)
The label variable of loan_default is:
Default is the target Variable in dataset where 1
displays bank loan default and 0 displays bank loan non default.
The source for this information can be found in a PDF called BankLoanDefaultDataset-Description.pdf
Working Dataset
For the purposes of demonstrating imputation in the future, missing
values were generated for each of Gender,
Marital_status, Emp_status,
Emp_duration, and Age to generate the working
dataset loan_default_Mod01.
# ====================
# DATA INTAKE
# ====================
loan_default <- read.csv("https://raw.githubusercontent.com/saltwatersoup/MyCV/refs/heads/main/Data%20Sets/Loan%20Default%20Data/BankLoanDefaultDataset.csv")
# ====================
# DATA MODIFICATION
# Adding missing values
# ====================
# Copying loan_default
loan_default_Mod01 <- loan_default
# Creating random observation IDs and replacing the corresponding observations with missing
# loan_default_Mod01$Checking_Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Term[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Credit_score[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Gender[sample(1:1000, 68, replace = FALSE)] <- NA
loan_default_Mod01$Marital_status[sample(1:1000, 87, replace = FALSE)] <- NA
# loan_default_Mod01$Car_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Personal_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Home_loan[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Education_loan[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Emp_status[sample(1:1000, 136, replace = FALSE)] <- NA
# loan_default_Mod01$Amount[sample(1:1000, 100, replace = FALSE)] <- NA
# loan_default_Mod01$Saving_amoun[sample(1:1000, 100, replace = FALSE)] <- NA
loan_default_Mod01$Emp_duration[sample(1:1000, 201, replace = FALSE)] <- NA
loan_default_Mod01$Age[sample(1:1000, 159, replace = FALSE)] <- NA
# loan_default_Mod01$No_of_credit_account[sample(1:1000, 100, replace = FALSE)] <- NA
Distribution of
Individual Features
Preparation for analysis calls for the following analytic tasks:
- Handling Missing Values
- Addressing Outliers
Handling Missing
Values
loan_default_Mod01 has missing values, in the variables
Gender, Marital_status,
Emp_status, Emp_duration, and
Age. To utilize observations with missing data, imputation
will be performed prior to analysis.
# ====================
# PLOTTING MISSING VALUES
# ====================
# Generating data frame of missing values per variable
MissDatCounts <- data.frame(
Variables = names(loan_default_Mod01),
Missing = colSums(is.na(loan_default_Mod01))
)
# Generating interactive plot using plotly
Plot_MissingVals <-
# Taking a subset of MissDatCounts, so only entries with > 0 missing values will be displayed
subset(MissDatCounts, Missing > 0) %>%
# Passing the subset to plot_ly
plot_ly(
x = ~Variables,
y = ~Missing
) %>%
layout(
title = list(
text = "Missing Values per Variable"
),
xaxis = list(
title = "Variables with Missing Values",
categoryorder = "trace"
),
yaxis = list(
title = "Number of Missing Values"
)
)
# Outputting plot
Plot_MissingVals
Addressing
Outliers
Of particular note is the upper end of credit scores. Typical credit
scores (FICO credit scores) range from 300 to 850. However, there are
some less common scoring models that utilize different numerical ranges.
The FICO NextGen ranges from 150 to 950. Though discontinued,
VantageScore 1.0 and 2.0 ranged from 501 to 990.
There are some Credit_score values that exceed 990, but
it is possible that those values may be the result of some rarer scoring
models. If such a scoring model cannot be identified, then it may be
worthwhile to convert credit score values over 990 to missing
values.
# ====================
# PLOTTING ALL NUMERICAL NON-BINARY VARIABLES
# ====================
# Selecting only numeric variables
NumVars <- select(loan_default_Mod01, where(is.numeric))
# Selecting eliminating any binary variables
NumVars <- NumVars[!apply(NumVars, 2, function(x){all(match(x, c(0, 1, NA), nomatch = FALSE))})]
# Preparing a list of subplots
NumFig <- c()
# Using a for loop to generate a subplot per variable in NumVars
for(i in 1:length(names(NumVars))){
NumFig[[i]] <- plot_ly(
x = NumVars[[i]],
y = "",
type = "box",
name = colnames(NumVars)[i]
)
}
# Generating a plot that contains 8 subplots (one for each variable in NumVars) across 4 rows
Plot_NumVars <-
subplot(NumFig[[1]], NumFig[[2]], NumFig[[3]], NumFig[[4]], NumFig[[5]], NumFig[[6]], NumFig[[7]], NumFig[[8]], nrows = 4, margin = 0.05) %>%
layout(
title = "Distributions of All Numerical Non-binary Variables",
legend = list(
title = list(text = "<b> Variable </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
Plot_NumVars
Relationship Between
Features
This section explores the potential relationships between feature
variables, specifically:
Checking_amount and Credit_score
Marital_status and Emp_status
Marital_status and Saving_amount
Checking_amount, Credit_score, and
Saving_amount
Checking_amount and Credit_score
It appears that higher checking amounts are associated with a
narrower range of credit scores. Therefore, there may be an association
between checking amount and credit score that is worth looking into.
# ====================
# PLOTTING `Checking_amount` AND `Credit_score`
# ====================
# Generating plot
FeatRel01 <-
plot_ly(
data = loan_default_Mod01,
x = ~Checking_amount,
y = ~Credit_score
) %>%
layout(
title = "Checking Amount and Credit Score",
xaxis = list(title = "Checking Amount"),
yaxis = list(title = "Credit Score")
)
# Outputting plot
FeatRel01
Marital_status and Emp_status
For all marital statuses, unemployed outnumbers both employed and
unknown. However, married-unemployed individuals outnumber every other
category. Associations like this may prove useful for imputation of
unknowns.
# ====================
# PLOTTING `Marital_status` AND `Emp_status`
# ====================
# Preparing plot data
MarStat_EmpStat <-
# Subsetting loan_default_Mod01 to just Marital_status and Saving_amount
loan_default_Mod01[ , c("Marital_status", "Emp_status")] %>%
# Replacing missing values with the word "Unknown"
replace_na(list(Marital_status = "Unknown", Emp_status = "Unknown")) %>%
# Grouping data by Marital_status and Emp_status
group_by(Marital_status, Emp_status) %>%
# Counting the number of entries in each subgroup
summarise(Count = n()) %>%
# Capitalizing "employed"
mutate(Emp_status = str_replace(Emp_status, "^employed", "Employed")) %>%
# Capitalizing "unemployed"
mutate(Emp_status = str_replace(Emp_status, "^unemployed", "Unemployed"))
# Generating plot
FeatRel02 <- plot_ly()
FeatRel02 <- FeatRel02 %>%
add_trace(
data = subset(MarStat_EmpStat, Emp_status == "Employed"),
x = ~Marital_status,
y = ~Count,
name = "Employed"
) %>%
add_trace(
data = subset(MarStat_EmpStat, Emp_status == "Unemployed"),
x = ~Marital_status,
y = ~Count,
name = "Unemployed"
) %>%
add_trace(
data = subset(MarStat_EmpStat, Emp_status == "Unknown"),
x = ~Marital_status,
y = ~Count,
name = "Unknown"
) %>%
layout(
title = "Marital Status and Employment Status",
xaxis = list(title = "Marital Status"),
yaxis = list(title = "Count"),
legend = list(
title = list(text = "<b> Employment Status </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
FeatRel02
Marital_status and Saving_amount
Savings amounts do not appear to change according to marital
status.
# ====================
# PLOTTING `Marital_status` AND `Saving_amount`
# ====================
# Preparing plot data
MarStat_SavAmou <-
# Subsetting loan_default_Mod01 to just Marital_status and Saving_amount
loan_default_Mod01[ , c("Marital_status", "Saving_amount")] %>%
# Replacing missing values with the word "Unknown"
replace_na(list(Marital_status = "Unknown"))
# Generating plot
FeatRel03 <- plot_ly() %>%
add_trace(
data = MarStat_SavAmou,
x = ~Saving_amount,
y = ~Marital_status,
type = "box",
color = ~Marital_status
) %>%
layout(
title = "Marital Status and Savings Amount",
xaxis = list(title = "Savings Amount"),
yaxis = list(
title = "Marital Status",
showticklabels = FALSE,
categoryarray = list("Unknown", "Single", "Married"),
categoryorder = "array"
),
legend = list(
title = list(text = "<b> Marital Status </b>"),
bgcolor = "#E2E2E2",
bordercolor = "#FFFFFF",
borderwidth = 2
)
)
# Outputting plot
FeatRel03
Checking_amount, Credit_score, and
Saving_amount
There do not appear to be more than one cluster of points or any
other form of clear divide that would indicate additional categories for
us to discover from this graph.
# ====================
# PLOTTING `Checking_amount`, `Credit_score`, AND `Saving_amount`
# ====================
# Preparing plot data
ChecAmou_CredSco_SavAmou <-
# Subsetting loan_default_Mod01 to just Checking_amount, Credit_score, and Saving_amount
loan_default_Mod01[ , c("Checking_amount", "Credit_score", "Saving_amount")]
# Generating plot
FeatRel04 <- plot_ly() %>%
add_trace(
data = ChecAmou_CredSco_SavAmou,
x = ~Checking_amount,
y = ~Credit_score,
z = ~Saving_amount,
marker = list(size = 2),
hovertemplate = paste(
"<b>Checking Amt</b>: %{x}<br>",
"<b>Credit Score</b>: %{y}<br>",
"<b>Saving Amt</b>: %{z}"
),
name = ""
) %>%
layout(
title = "Checking Amount, Credit Score, and Savings Amount",
scene = list(
xaxis = list(title = "Checking Amount"),
yaxis = list(title = "Credit Score"),
zaxis = list(title = "Savings Amount"),
aspectmode = "cube"
)
)
# Outputting plot
FeatRel04
LS0tDQp0aXRsZTogIkxvYW4gRGVmYXVsdCBEYXRhIFtJbnRha2UgYW5kIE92ZXJ2aWV3XSINCmF1dGhvcjogIktvamkgU2hpbW9tdXJhIg0KZGF0ZTogIiAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRvY19jb2xsYXBzZWQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIHRoZW1lOiBsdW1lbg0KICB3b3JkX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBrZWVwX21kOiB5ZXMNCiAgcGRmX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19kZXB0aDogNA0KICAgIGZpZ19jYXB0aW9uOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vDQogICAgZmlnX3dpZHRoOiAzDQogICAgZmlnX2hlaWdodDogMw0KZWRpdG9yX29wdGlvbnM6IA0KICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lDQotLS0NCg0KYGBgez1odG1sfQ0KDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQovKiBDYXNjYWRpbmcgU3R5bGUgU2hlZXRzIChDU1MpIGlzIGEgc3R5bGVzaGVldCBsYW5ndWFnZSB1c2VkIHRvIGRlc2NyaWJlIHRoZSBwcmVzZW50YXRpb24gb2YgYSBkb2N1bWVudCB3cml0dGVuIGluIEhUTUwgb3IgWE1MLiBpdCBpcyBhIHNpbXBsZSBtZWNoYW5pc20gZm9yIGFkZGluZyBzdHlsZSAoZS5nLiwgZm9udHMsIGNvbG9ycywgc3BhY2luZykgdG8gV2ViIGRvY3VtZW50cy4gKi8NCg0KaDEudGl0bGUgeyAgLyogVGl0bGUgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIHRoZSByZXBvcnQgdGl0bGUgKi8NCiAgZm9udC1zaXplOiAyMnB4Ow0KICBmb250LXdlaWdodDogYm9sZDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgZm9udC1mYW1pbHk6ICJHaWxsIFNhbnMiLCBzYW5zLXNlcmlmOw0KfQ0KaDQuYXV0aG9yIHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIGZvciBhdXRob3JzICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogbmF2eTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDEgeyAvKiBIZWFkZXIgMSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDEgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMiAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgZm9yIGxldmVsIDIgc2VjdGlvbiB0aXRsZSAqLw0KICAgIGZvbnQtc2l6ZTogMjBweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KICAgIGZvbnQtd2VpZ2h0OiBib2xkOw0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBvZiBsZXZlbCAzIHNlY3Rpb24gdGl0bGUgICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDQgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KYm9keSB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KLmhpZ2hsaWdodG1lIHsgYmFja2dyb3VuZC1jb2xvcjp5ZWxsb3c7IH0NCg0KcCB7IGJhY2tncm91bmQtY29sb3I6d2hpdGU7IH0NCg0KPC9zdHlsZT4NCmBgYA0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoInBhbG1lcnBlbmd1aW5zIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBhbG1lcnBlbmd1aW5zIikNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoIkdHYWxseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KbGlicmFyeShHR2FsbHkpDQp9DQppZiAoIXJlcXVpcmUoIm5hbmlhciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJuYW5pYXIiKQ0KbGlicmFyeShuYW5pYXIpDQp9DQppZiAoIXJlcXVpcmUoInBvb2wiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicG9vbCIpDQpsaWJyYXJ5KHBvb2wpDQp9DQppZiAoIXJlcXVpcmUoIkRCSSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJEQkkiKQ0KbGlicmFyeShEQkkpDQp9DQppZiAoIXJlcXVpcmUoIlJNeVNRTCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJSTXlTUUwiKQ0KbGlicmFyeShSTXlTUUwpDQp9DQppZiAoIXJlcXVpcmUoInJhbmRvbUZvcmVzdCIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJyYW5kb21Gb3Jlc3QiKQ0KbGlicmFyeShyYW5kb21Gb3Jlc3QpDQp9DQppZiAoIXJlcXVpcmUoImdnaXJhcGgiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dpcmFwaCIpDQpsaWJyYXJ5KGdnaXJhcGgpDQp9DQppZiAoIXJlcXVpcmUoImhpZ2hjaGFydGVyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImhpZ2hjaGFydGVyIikNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpDQp9DQppZiAoIXJlcXVpcmUoImJyb29tIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImJyb29tIikNCmxpYnJhcnkoYnJvb20pDQp9DQojIyANCmtuaXRyOjpvcHRzX2NodW5rJHNldCgNCiAgIyBpbmNsdWRlIGNvZGUgY2h1bmsgaW4gdGhlIG91dHB1dCBmaWxlDQogIGVjaG8gPSBUUlVFLA0KICAjIHNvbWV0aW1lcywgeW91IGNvZGUgbWF5IHByb2R1Y2Ugd2FybmluZyBtZXNzYWdlcywgeW91IGNhbiBjaG9vc2UgdG8gaW5jbHVkZSB0aGUgd2FybmluZyBtZXNzYWdlcyBpbiB0aGUgb3V0cHV0IGZpbGUuDQogIHdhcm5pbmcgPSBGQUxTRSwgDQogICMgeW91IGNhbiBhbHNvIGRlY2lkZSB3aGV0aGVyIHRvIGluY2x1ZGUgdGhlIG91dHB1dCBpbiB0aGUgb3V0cHV0IGZpbGUuDQogIHJlc3VsdHMgPSBUUlVFLCANCiAgbWVzc2FnZSA9IEZBTFNFLA0KICBjb21tZW50ID0gTkENCikgIA0KYGBgDQoNCiMgSW50cm9kdWN0aW9uDQoNClRoZSBkYXRhIHV0aWxpemVkIGluIHRoaXMgYW5hbHlzaXMgaXMgZnJvbSBhIGRhdGFzZXQgY2FsbGVkIGBsb2FuX2RlZmF1bHRgIGZyb20gKkFwcGxpZWQgQW5hbHl0aWNzIHRocm91Z2ggQ2FzZSBTdHVkaWVzIFVzaW5nIFNBUyBhbmQgUiosIGJ5IERlZXB0aSBHdXB0YSwgcHVibGlzaGVkIGJ5IEFQcmVzcywgSVNCTiAtIDk3OC0xLTQ4NDItMzUyNS02Lg0KDQojIyBQdXJwb3NlIG9mIERhdGEgQ29sbGVjdGlvbg0KPCEtLSANClByb3ZpZGUgYSBjbGVhciBhbmQgY29uY2lzZSBleHBsYW5hdGlvbiBvZiB3aHkgdGhlIGRhdGEgaXMgYmVpbmcgY29sbGVjdGVkLCBoaWdobGlnaHRpbmcgdGhlIHNwZWNpZmljIG9iamVjdGl2ZXMgYW5kIGludGVuZGVkIHVzZSBvZiB0aGUgZGF0YS4gDQotLT4NCg0KVGhlIGRhdGEgZm9yIGBsb2FuX2RlZmF1bHRgIHdhcyBjb2xsZWN0ZWQgdG8gZ2VuZXJhdGUgYSBtb2RlbCBmb3IgcHJlZGljdGluZyB3aGljaCBjdXN0b21lcnMgYXJlIG1vc3QgbGlrZWx5IHRvIGRlZmF1bHQuDQoNCiMjIERlc2NyaXB0aW9uIG9mIHRoZSBEYXRhIENvbGxlY3Rpb24gb3IgR2VuZXJhdGlvbiBQcm9jZXNzDQo8IS0tIA0KT3V0bGluZSB0aGUgbWV0aG9kcyB1c2VkIHRvIGNvbGxlY3Qgb3IgZ2VuZXJhdGUgdGhlIGRhdGEsIGluY2x1ZGluZyBhbnkgdG9vbHMsIHRlY2hub2xvZ2llcywgb3IgcHJvdG9jb2xzIGZvbGxvd2VkLiBTcGVjaWZ5IHRoZSB0aW1lIGZyYW1lIGFuZCBsb2NhdGlvbiwgaWYgYXBwbGljYWJsZS4gDQotLT4NCg0KVGhlIGV4YWN0IG1ldGhvZHMgdXNlZCB0byBjb2xsZWN0IHRoZSBkYXRhIGluIGBsb2FuX2RlZmF1bHRgIGFyZSB1bmZvcnR1bmF0ZWx5IHVua25vd24uDQoNCiMjIFNhbXBsZSBTaXplIGFuZCBOdW1iZXIgb2YgRmVhdHVyZSBWYXJpYWJsZXMNCjwhLS0gDQpTdGF0ZSB0aGUgdG90YWwgc2FtcGxlIHNpemUgYW5kIHRoZSBudW1iZXIgb2YgZmVhdHVyZSB2YXJpYWJsZXMgaW5jbHVkZWQgaW4gdGhlIGRhdGEgc2V0LCBwcm92aWRpbmcgY29udGV4dCBmb3IgdGhlIHNjb3BlIGFuZCByZXByZXNlbnRhdGl2ZW5lc3Mgb2YgdGhlIGRhdGEuIA0KLS0+DQoNCmBsb2FuX2RlZmF1bHRgIGhhcyBhIHRvdGFsIHNhbXBsZSBzaXplIG9mIDEsMDAwLCBjb250YWlucyAxNSBmZWF0dXJlIHZhcmlhYmxlcywgYW5kIGhhcyAxIGxhYmVsLg0KDQojIyBJdGVtaXplZCBMaXN0IG9mIEZlYXR1cmUgVmFyaWFibGVzDQo8IS0tIA0KUHJlc2VudCBhIGRldGFpbGVkIGxpc3Qgb2YgZmVhdHVyZSB2YXJpYWJsZXMsIGluY2x1ZGluZzoNCiogRGVmaW5pdGlvbi9EZXNjcmlwdGlvbjogUHJvdmlkZSBhIGJyaWVmIGV4cGxhbmF0aW9uIG9mIHdoYXQgZWFjaCB2YXJpYWJsZSByZXByZXNlbnRzLg0KKiBEYXRhIFR5cGVzOiBTcGVjaWZ5IHRoZSB0eXBlIG9mIGRhdGEgKGUuZy4sIGNhdGVnb3JpY2FsLCBudW1lcmljYWwsIGJvb2xlYW4sIHRleHQpLiANCi0tPg0KDQpUaGUgZmVhdHVyZSB2YXJpYWJsZXMgb2YgYGxvYW5fZGVmYXVsdGAgYXJlIGFzIGZvbGxvd3M6DQoNCiogVmFyaWFibGUgMTogYENoZWNraW5nX0Ftb3VudGAgKE51bWVyaWMpDQoqIFZhcmlhYmxlIDI6IGBUZXJtYCAoZGlzcGxheWVkIGluIG1vbnRocyAoTnVtZXJpYykpDQoqIFZhcmlhYmxlIDM6IGBDcmVkaXRfc2NvcmVgIChOdW1lcmljKQ0KKiBWYXJpYWJsZSA0OiBgR2VuZGVyYCAoQ2F0ZWdvcmljYWwpDQoqIFZhcmlhYmxlIDU6IGBNYXJpdGFsX3N0YXR1c2AgKENhdGVnb3JpY2FsKQ0KKiBWYXJpYWJsZSA2OiBgQ2FyX2xvYW5gICgxLSBPd24gY2FyIGxvYW4sIDAtIERvZXMgbm90IG93biBjYXIgbG9hbiDigJMNCk51bWVyaWMpDQoqIFZhcmlhYmxlIDc6IGBQZXJzb25hbF9sb2FuYCAoMS0gT3duIFBlcnNvbmFsIGxvYW4sIDAtIERvZXMgbm90IG93bg0KUGVyc29uYWwgbG9hbiDigJMgTnVtZXJpYykNCiogVmFyaWFibGUgODogYEhvbWVfbG9hbmAgKDEtIE93biBIb21lIGxvYW4sIDAtIERvZXMgbm90IG93biBIb21lDQpsb2FuIOKAkyBOdW1lcmljKQ0KKiBWYXJpYWJsZSA5OiBgRWR1Y2F0aW9uX2xvYW5gICgxLSBPd24gRWR1Y2F0aW9uIGxvYW4sIDAtIERvZXMgbm90DQpvd24gRWR1Y2F0aW9uIGxvYW4g4oCTIE51bWVyaWMpDQoqIFZhcmlhYmxlIDEwOiBgRW1wX3N0YXR1c2AgKENhdGVnb3JpY2FsKQ0KKiBWYXJpYWJsZSAxMTogYEFtb3VudGAgKE51bWVyaWMpDQoqIFZhcmlhYmxlIDEyOiBgU2F2aW5nX2Ftb3VuYCAoTnVtZXJpYykNCiogVmFyaWFibGUgMTM6IGBFbXBfZHVyYXRpb25gICh3aGljaCBpcyBkaXNwbGF5ZWQgaW4gbW9udGhzDQooTnVtZXJpYykpDQoqIFZhcmlhYmxlIDE0OiBgQWdlYCAod2hpY2ggaXMgZGlzcGxheWVkIGluIHllYXJzIChOdW1lcmljKSkNCiogVmFyaWFibGUgMTU6IGBOb19vZl9jcmVkaXRfYWNjb3VudGAgKE51bWVyaWMpDQoNClRoZSBsYWJlbCB2YXJpYWJsZSBvZiBgbG9hbl9kZWZhdWx0YCBpczoNCg0KKiBgRGVmYXVsdGAgaXMgdGhlIHRhcmdldCBWYXJpYWJsZSBpbiBkYXRhc2V0IHdoZXJlIDEgZGlzcGxheXMgYmFuayBsb2FuDQpkZWZhdWx0IGFuZCAwIGRpc3BsYXlzIGJhbmsgbG9hbiBub24gZGVmYXVsdC4NCg0KVGhlIHNvdXJjZSBmb3IgdGhpcyBpbmZvcm1hdGlvbiBjYW4gYmUgZm91bmQgaW4gYSBQREYgY2FsbGVkIFtCYW5rTG9hbkRlZmF1bHREYXRhc2V0LURlc2NyaXB0aW9uLnBkZl0oaHR0cHM6Ly9naXRodWIuY29tL3NhbHR3YXRlcnNvdXAvTXlDVi9ibG9iLzhjNWQwODE3OWU3MTIwMTI2NTM3ZjZkYjA3Y2MwNGU1YTc0MTEzN2UvRGF0YSUyMFNldHMvTG9hbiUyMERlZmF1bHQlMjBEYXRhL0JhbmtMb2FuRGVmYXVsdERhdGFzZXQtRGVzY3JpcHRpb24ucGRmKQ0KDQojIFdvcmtpbmcgRGF0YXNldA0KDQpGb3IgdGhlIHB1cnBvc2VzIG9mIGRlbW9uc3RyYXRpbmcgaW1wdXRhdGlvbiBpbiB0aGUgZnV0dXJlLCBtaXNzaW5nIHZhbHVlcyB3ZXJlIGdlbmVyYXRlZCBmb3IgZWFjaCBvZiBgR2VuZGVyYCwgYE1hcml0YWxfc3RhdHVzYCwgYEVtcF9zdGF0dXNgLCBgRW1wX2R1cmF0aW9uYCwgYW5kIGBBZ2VgIHRvIGdlbmVyYXRlIHRoZSB3b3JraW5nIGRhdGFzZXQgYGxvYW5fZGVmYXVsdF9Nb2QwMWAuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBEQVRBIElOVEFLRQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQpsb2FuX2RlZmF1bHQgPC0gcmVhZC5jc3YoImh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9zYWx0d2F0ZXJzb3VwL015Q1YvcmVmcy9oZWFkcy9tYWluL0RhdGElMjBTZXRzL0xvYW4lMjBEZWZhdWx0JTIwRGF0YS9CYW5rTG9hbkRlZmF1bHREYXRhc2V0LmNzdiIpDQpgYGANCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIERBVEEgTU9ESUZJQ0FUSU9ODQojIEFkZGluZyBtaXNzaW5nIHZhbHVlcw0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIENvcHlpbmcgbG9hbl9kZWZhdWx0DQpsb2FuX2RlZmF1bHRfTW9kMDEgPC0gbG9hbl9kZWZhdWx0DQoNCiMgQ3JlYXRpbmcgcmFuZG9tIG9ic2VydmF0aW9uIElEcyBhbmQgcmVwbGFjaW5nIHRoZSBjb3JyZXNwb25kaW5nIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcNCg0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkQ2hlY2tpbmdfQW1vdW50W3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAxJFRlcm1bc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkQ3JlZGl0X3Njb3JlW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMSRHZW5kZXJbc2FtcGxlKDE6MTAwMCwgNjgsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDEkTWFyaXRhbF9zdGF0dXNbc2FtcGxlKDE6MTAwMCwgODcsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRDYXJfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRQZXJzb25hbF9sb2FuW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCiMgbG9hbl9kZWZhdWx0X01vZDAxJEhvbWVfbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRFZHVjYXRpb25fbG9hbltzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpsb2FuX2RlZmF1bHRfTW9kMDEkRW1wX3N0YXR1c1tzYW1wbGUoMToxMDAwLCAxMzYsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSRBbW91bnRbc2FtcGxlKDE6MTAwMCwgMTAwLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KIyBsb2FuX2RlZmF1bHRfTW9kMDEkU2F2aW5nX2Ftb3VuW3NhbXBsZSgxOjEwMDAsIDEwMCwgcmVwbGFjZSA9IEZBTFNFKV0gPC0gTkENCmxvYW5fZGVmYXVsdF9Nb2QwMSRFbXBfZHVyYXRpb25bc2FtcGxlKDE6MTAwMCwgMjAxLCByZXBsYWNlID0gRkFMU0UpXSA8LSBOQQ0KbG9hbl9kZWZhdWx0X01vZDAxJEFnZVtzYW1wbGUoMToxMDAwLCAxNTksIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQojIGxvYW5fZGVmYXVsdF9Nb2QwMSROb19vZl9jcmVkaXRfYWNjb3VudFtzYW1wbGUoMToxMDAwLCAxMDAsIHJlcGxhY2UgPSBGQUxTRSldIDwtIE5BDQpgYGANCg0KIyBEaXN0cmlidXRpb24gb2YgSW5kaXZpZHVhbCBGZWF0dXJlcw0KDQpQcmVwYXJhdGlvbiBmb3IgYW5hbHlzaXMgY2FsbHMgZm9yIHRoZSBmb2xsb3dpbmcgYW5hbHl0aWMgdGFza3M6DQoNCjEuIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWVzDQoyLiBBZGRyZXNzaW5nIE91dGxpZXJzDQoNCiMjIEhhbmRsaW5nIE1pc3NpbmcgVmFsdWVzDQoNCmBsb2FuX2RlZmF1bHRfTW9kMDFgIGhhcyBtaXNzaW5nIHZhbHVlcywgaW4gdGhlIHZhcmlhYmxlcyBgR2VuZGVyYCwgYE1hcml0YWxfc3RhdHVzYCwgYEVtcF9zdGF0dXNgLCBgRW1wX2R1cmF0aW9uYCwgYW5kIGBBZ2VgLiBUbyB1dGlsaXplIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgZGF0YSwgaW1wdXRhdGlvbiB3aWxsIGJlIHBlcmZvcm1lZCBwcmlvciB0byBhbmFseXNpcy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIE1JU1NJTkcgVkFMVUVTDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBkYXRhIGZyYW1lIG9mIG1pc3NpbmcgdmFsdWVzIHBlciB2YXJpYWJsZQ0KTWlzc0RhdENvdW50cyA8LSBkYXRhLmZyYW1lKA0KICBWYXJpYWJsZXMgPSBuYW1lcyhsb2FuX2RlZmF1bHRfTW9kMDEpLA0KICBNaXNzaW5nID0gY29sU3Vtcyhpcy5uYShsb2FuX2RlZmF1bHRfTW9kMDEpKQ0KKQ0KDQojIEdlbmVyYXRpbmcgaW50ZXJhY3RpdmUgcGxvdCB1c2luZyBwbG90bHkNClBsb3RfTWlzc2luZ1ZhbHMgPC0gDQogICMgVGFraW5nIGEgc3Vic2V0IG9mIE1pc3NEYXRDb3VudHMsIHNvIG9ubHkgZW50cmllcyB3aXRoID4gMCBtaXNzaW5nIHZhbHVlcyB3aWxsIGJlIGRpc3BsYXllZA0KICBzdWJzZXQoTWlzc0RhdENvdW50cywgTWlzc2luZyA+IDApICU+JSANCiAgIyBQYXNzaW5nIHRoZSBzdWJzZXQgdG8gcGxvdF9seQ0KICBwbG90X2x5KA0KICAgIHggPSB+VmFyaWFibGVzLA0KICAgIHkgPSB+TWlzc2luZw0KICApICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gbGlzdCgNCiAgICAgIHRleHQgPSAiTWlzc2luZyBWYWx1ZXMgcGVyIFZhcmlhYmxlIg0KICAgICksDQogICAgeGF4aXMgPSBsaXN0KA0KICAgICAgdGl0bGUgPSAiVmFyaWFibGVzIHdpdGggTWlzc2luZyBWYWx1ZXMiLA0KICAgICAgY2F0ZWdvcnlvcmRlciA9ICJ0cmFjZSINCiAgICApLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk51bWJlciBvZiBNaXNzaW5nIFZhbHVlcyINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTWlzc2luZ1ZhbHMNCmBgYA0KDQojIyBBZGRyZXNzaW5nIE91dGxpZXJzDQoNCk9mIHBhcnRpY3VsYXIgbm90ZSBpcyB0aGUgdXBwZXIgZW5kIG9mIGNyZWRpdCBzY29yZXMuIFR5cGljYWwgY3JlZGl0IHNjb3JlcyAoRklDTyBjcmVkaXQgc2NvcmVzKSByYW5nZSBmcm9tIDMwMCB0byA4NTAuIEhvd2V2ZXIsIHRoZXJlIGFyZSBzb21lIGxlc3MgY29tbW9uIHNjb3JpbmcgbW9kZWxzIHRoYXQgdXRpbGl6ZSBkaWZmZXJlbnQgbnVtZXJpY2FsIHJhbmdlcy4gVGhlIEZJQ08gTmV4dEdlbiByYW5nZXMgZnJvbSAxNTAgdG8gOTUwLiBUaG91Z2ggZGlzY29udGludWVkLCBWYW50YWdlU2NvcmUgMS4wIGFuZCAyLjAgcmFuZ2VkIGZyb20gNTAxIHRvIDk5MC4NCg0KVGhlcmUgYXJlIHNvbWUgYENyZWRpdF9zY29yZWAgdmFsdWVzIHRoYXQgZXhjZWVkIDk5MCwgYnV0IGl0IGlzIHBvc3NpYmxlIHRoYXQgdGhvc2UgdmFsdWVzIG1heSBiZSB0aGUgcmVzdWx0IG9mIHNvbWUgcmFyZXIgc2NvcmluZyBtb2RlbHMuIElmIHN1Y2ggYSBzY29yaW5nIG1vZGVsIGNhbm5vdCBiZSBpZGVudGlmaWVkLCB0aGVuIGl0IG1heSBiZSB3b3J0aHdoaWxlIHRvIGNvbnZlcnQgY3JlZGl0IHNjb3JlIHZhbHVlcyBvdmVyIDk5MCB0byBtaXNzaW5nIHZhbHVlcy4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIEFMTCBOVU1FUklDQUwgTk9OLUJJTkFSWSBWQVJJQUJMRVMNCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAxLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBsaXN0IG9mIHN1YnBsb3RzDQpOdW1GaWcgPC0gYygpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gZ2VuZXJhdGUgYSBzdWJwbG90IHBlciB2YXJpYWJsZSBpbiBOdW1WYXJzDQpmb3IoaSBpbiAxOmxlbmd0aChuYW1lcyhOdW1WYXJzKSkpew0KICBOdW1GaWdbW2ldXSA8LSBwbG90X2x5KA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgIHkgPSAiIiwgDQogICAgdHlwZSA9ICJib3giLA0KICAgIG5hbWUgPSBjb2xuYW1lcyhOdW1WYXJzKVtpXQ0KICApDQp9DQoNCiMgR2VuZXJhdGluZyBhIHBsb3QgdGhhdCBjb250YWlucyA4IHN1YnBsb3RzIChvbmUgZm9yIGVhY2ggdmFyaWFibGUgaW4gTnVtVmFycykgYWNyb3NzIDQgcm93cw0KUGxvdF9OdW1WYXJzIDwtIA0KICBzdWJwbG90KE51bUZpZ1tbMV1dLCBOdW1GaWdbWzJdXSwgTnVtRmlnW1szXV0sIE51bUZpZ1tbNF1dLCBOdW1GaWdbWzVdXSwgTnVtRmlnW1s2XV0sIE51bUZpZ1tbN11dLCBOdW1GaWdbWzhdXSwgbnJvd3MgPSA0LCBtYXJnaW4gPSAwLjA1KSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJEaXN0cmlidXRpb25zIG9mIEFsbCBOdW1lcmljYWwgTm9uLWJpbmFyeSBWYXJpYWJsZXMiLA0KICAgIGxlZ2VuZCA9IGxpc3QoDQogICAgICB0aXRsZSA9IGxpc3QodGV4dCA9ICI8Yj4gVmFyaWFibGUgPC9iPiIpLA0KICAgICAgYmdjb2xvciA9ICIjRTJFMkUyIiwNCiAgICAgIGJvcmRlcmNvbG9yID0gIiNGRkZGRkYiLA0KICAgICAgYm9yZGVyd2lkdGggPSAyDQogICAgKQ0KICApDQoNCiMgT3V0cHV0dGluZyBwbG90DQpQbG90X051bVZhcnMNCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBBTEwgTlVNRVJJQ0FMIE5PTi1CSU5BUlkgVkFSSUFCTEVTIE9OIFNBTUUgQVhJUw0KIyBUaGlzIGlzIG15IGluaXRpYWwgdmVyc2lvbiBvZiB0aGUgcGxvdCwgd2hpY2ggaXNuJ3QgcGFydGljdWxhcmx5IHVzZWZ1bCBiZWNhdXNlIHRoZSByYW5nZXMgZm9yIGVhY2ggdmFyaWFibGUgYXJlIHNvIGRpZmZlcmVudC4NCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBTZWxlY3Rpbmcgb25seSBudW1lcmljIHZhcmlhYmxlcw0KTnVtVmFycyA8LSBzZWxlY3QobG9hbl9kZWZhdWx0X01vZDAxLCB3aGVyZShpcy5udW1lcmljKSkNCiMgU2VsZWN0aW5nIGVsaW1pbmF0aW5nIGFueSBiaW5hcnkgdmFyaWFibGVzDQpOdW1WYXJzIDwtIE51bVZhcnNbIWFwcGx5KE51bVZhcnMsIDIsIGZ1bmN0aW9uKHgpe2FsbChtYXRjaCh4LCBjKDAsIDEsIE5BKSwgbm9tYXRjaCA9IEZBTFNFKSl9KV0NCg0KIyBQcmVwYXJpbmcgYSBwbG90DQpQbG90X051bVZhcnMgPC0gcGxvdF9seSgpDQojIFVzaW5nIGEgZm9yIGxvb3AgdG8gYWRkIGVhY2ggTnVtVmFycyB2YXJpYWJsZSB0byB0aGUgcGxvdA0KZm9yKGkgaW4gMTpsZW5ndGgobmFtZXMoTnVtVmFycykpKXsNCiAgUGxvdF9OdW1WYXJzIDwtIGFkZF90cmFjZSgNCiAgICBkYXRhID0gUGxvdF9OdW1WYXJzLA0KICAgIHggPSBOdW1WYXJzW1tpXV0sIA0KICAgIHR5cGUgPSAiYm94IiwgDQogICAgbmFtZSA9IGNvbG5hbWVzKE51bVZhcnMpW2ldDQogICkNCn0NCg0KIyBPdXRwdXR0aW5nIHBsb3QNClBsb3RfTnVtVmFycw0KYGBgDQoNCiMgUmVsYXRpb25zaGlwIEJldHdlZW4gRmVhdHVyZXMNCg0KVGhpcyBzZWN0aW9uIGV4cGxvcmVzIHRoZSBwb3RlbnRpYWwgcmVsYXRpb25zaGlwcyBiZXR3ZWVuIGZlYXR1cmUgdmFyaWFibGVzLCBzcGVjaWZpY2FsbHk6DQoNCjEuIGBDaGVja2luZ19hbW91bnRgIGFuZCBgQ3JlZGl0X3Njb3JlYA0KMi4gYE1hcml0YWxfc3RhdHVzYCBhbmQgYEVtcF9zdGF0dXNgDQozLiBgTWFyaXRhbF9zdGF0dXNgIGFuZCBgU2F2aW5nX2Ftb3VudGANCjQuIGBDaGVja2luZ19hbW91bnRgLCBgQ3JlZGl0X3Njb3JlYCwgYW5kIGBTYXZpbmdfYW1vdW50YA0KDQojIyBgQ2hlY2tpbmdfYW1vdW50YCBhbmQgYENyZWRpdF9zY29yZWANCg0KSXQgYXBwZWFycyB0aGF0IGhpZ2hlciBjaGVja2luZyBhbW91bnRzIGFyZSBhc3NvY2lhdGVkIHdpdGggYSBuYXJyb3dlciByYW5nZSBvZiBjcmVkaXQgc2NvcmVzLiBUaGVyZWZvcmUsIHRoZXJlIG1heSBiZSBhbiBhc3NvY2lhdGlvbiBiZXR3ZWVuIGNoZWNraW5nIGFtb3VudCBhbmQgY3JlZGl0IHNjb3JlIHRoYXQgaXMgd29ydGggbG9va2luZyBpbnRvLg0KDQpgYGB7cn0NCiMgPT09PT09PT09PT09PT09PT09PT0NCiMgUExPVFRJTkcgYENoZWNraW5nX2Ftb3VudGAgQU5EIGBDcmVkaXRfc2NvcmVgDQojID09PT09PT09PT09PT09PT09PT09DQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDEgPC0gDQogIHBsb3RfbHkoDQogICAgZGF0YSA9IGxvYW5fZGVmYXVsdF9Nb2QwMSwNCiAgICB4ID0gfkNoZWNraW5nX2Ftb3VudCwNCiAgICB5ID0gfkNyZWRpdF9zY29yZQ0KICApICU+JSANCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkNoZWNraW5nIEFtb3VudCBhbmQgQ3JlZGl0IFNjb3JlIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiQ2hlY2tpbmcgQW1vdW50IiksDQogICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIkNyZWRpdCBTY29yZSIpDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNCkZlYXRSZWwwMQ0KYGBgDQoNCiMjIGBNYXJpdGFsX3N0YXR1c2AgYW5kIGBFbXBfc3RhdHVzYA0KDQpGb3IgYWxsIG1hcml0YWwgc3RhdHVzZXMsIHVuZW1wbG95ZWQgb3V0bnVtYmVycyBib3RoIGVtcGxveWVkIGFuZCB1bmtub3duLiBIb3dldmVyLCBtYXJyaWVkLXVuZW1wbG95ZWQgaW5kaXZpZHVhbHMgb3V0bnVtYmVyIGV2ZXJ5IG90aGVyIGNhdGVnb3J5LiBBc3NvY2lhdGlvbnMgbGlrZSB0aGlzIG1heSBwcm92ZSB1c2VmdWwgZm9yIGltcHV0YXRpb24gb2YgdW5rbm93bnMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBgTWFyaXRhbF9zdGF0dXNgIEFORCBgRW1wX3N0YXR1c2ANCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBQcmVwYXJpbmcgcGxvdCBkYXRhDQpNYXJTdGF0X0VtcFN0YXQgPC0gDQogICMgU3Vic2V0dGluZyBsb2FuX2RlZmF1bHRfTW9kMDEgdG8ganVzdCBNYXJpdGFsX3N0YXR1cyBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiTWFyaXRhbF9zdGF0dXMiLCAiRW1wX3N0YXR1cyIpXSAlPiUgDQogICMgUmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHdvcmQgIlVua25vd24iDQogIHJlcGxhY2VfbmEobGlzdChNYXJpdGFsX3N0YXR1cyA9ICJVbmtub3duIiwgRW1wX3N0YXR1cyA9ICJVbmtub3duIikpICU+JQ0KICAjIEdyb3VwaW5nIGRhdGEgYnkgTWFyaXRhbF9zdGF0dXMgYW5kIEVtcF9zdGF0dXMNCiAgZ3JvdXBfYnkoTWFyaXRhbF9zdGF0dXMsIEVtcF9zdGF0dXMpICU+JSANCiAgIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIGVudHJpZXMgaW4gZWFjaCBzdWJncm91cA0KICBzdW1tYXJpc2UoQ291bnQgPSBuKCkpICU+JSANCiAgIyBDYXBpdGFsaXppbmcgImVtcGxveWVkIg0KICBtdXRhdGUoRW1wX3N0YXR1cyA9IHN0cl9yZXBsYWNlKEVtcF9zdGF0dXMsICJeZW1wbG95ZWQiLCAiRW1wbG95ZWQiKSkgJT4lIA0KICAjIENhcGl0YWxpemluZyAidW5lbXBsb3llZCINCiAgbXV0YXRlKEVtcF9zdGF0dXMgPSBzdHJfcmVwbGFjZShFbXBfc3RhdHVzLCAiXnVuZW1wbG95ZWQiLCAiVW5lbXBsb3llZCIpKQ0KDQojIEdlbmVyYXRpbmcgcGxvdA0KRmVhdFJlbDAyIDwtIHBsb3RfbHkoKQ0KRmVhdFJlbDAyIDwtIEZlYXRSZWwwMiAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiRW1wbG95ZWQiKSwNCiAgICB4ID0gfk1hcml0YWxfc3RhdHVzLA0KICAgIHkgPSB+Q291bnQsDQogICAgbmFtZSA9ICJFbXBsb3llZCINCiAgKSAlPiUgDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiVW5lbXBsb3llZCIpLA0KICAgIHggPSB+TWFyaXRhbF9zdGF0dXMsDQogICAgeSA9IH5Db3VudCwNCiAgICBuYW1lID0gIlVuZW1wbG95ZWQiDQogICkgJT4lDQogIGFkZF90cmFjZSgNCiAgICBkYXRhID0gc3Vic2V0KE1hclN0YXRfRW1wU3RhdCwgRW1wX3N0YXR1cyA9PSAiVW5rbm93biIpLA0KICAgIHggPSB+TWFyaXRhbF9zdGF0dXMsDQogICAgeSA9IH5Db3VudCwNCiAgICBuYW1lID0gIlVua25vd24iDQogICkgJT4lIA0KICBsYXlvdXQoDQogICAgdGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMgYW5kIEVtcGxveW1lbnQgU3RhdHVzIiwNCiAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiTWFyaXRhbCBTdGF0dXMiKSwNCiAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiQ291bnQiKSwNCiAgICBsZWdlbmQgPSBsaXN0KA0KICAgICAgdGl0bGUgPSBsaXN0KHRleHQgPSAiPGI+IEVtcGxveW1lbnQgU3RhdHVzIDwvYj4iKSwNCiAgICAgIGJnY29sb3IgPSAiI0UyRTJFMiIsDQogICAgICBib3JkZXJjb2xvciA9ICIjRkZGRkZGIiwNCiAgICAgIGJvcmRlcndpZHRoID0gMg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KRmVhdFJlbDAyDQpgYGANCg0KIyMgYE1hcml0YWxfc3RhdHVzYCBhbmQgYFNhdmluZ19hbW91bnRgDQoNClNhdmluZ3MgYW1vdW50cyBkbyBub3QgYXBwZWFyIHRvIGNoYW5nZSBhY2NvcmRpbmcgdG8gbWFyaXRhbCBzdGF0dXMuDQoNCmBgYHtyfQ0KIyA9PT09PT09PT09PT09PT09PT09PQ0KIyBQTE9UVElORyBgTWFyaXRhbF9zdGF0dXNgIEFORCBgU2F2aW5nX2Ftb3VudGANCiMgPT09PT09PT09PT09PT09PT09PT0NCg0KIyBQcmVwYXJpbmcgcGxvdCBkYXRhDQpNYXJTdGF0X1NhdkFtb3UgPC0gDQogICMgU3Vic2V0dGluZyBsb2FuX2RlZmF1bHRfTW9kMDEgdG8ganVzdCBNYXJpdGFsX3N0YXR1cyBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiTWFyaXRhbF9zdGF0dXMiLCAiU2F2aW5nX2Ftb3VudCIpXSAlPiUgDQogICMgUmVwbGFjaW5nIG1pc3NpbmcgdmFsdWVzIHdpdGggdGhlIHdvcmQgIlVua25vd24iDQogIHJlcGxhY2VfbmEobGlzdChNYXJpdGFsX3N0YXR1cyA9ICJVbmtub3duIikpDQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDMgPC0gcGxvdF9seSgpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIGRhdGEgPSBNYXJTdGF0X1NhdkFtb3UsDQogICAgeCA9IH5TYXZpbmdfYW1vdW50LCANCiAgICB5ID0gfk1hcml0YWxfc3RhdHVzLCANCiAgICB0eXBlID0gImJveCIsDQogICAgY29sb3IgPSB+TWFyaXRhbF9zdGF0dXMNCiAgKSAlPiUgDQogIGxheW91dCgNCiAgICB0aXRsZSA9ICJNYXJpdGFsIFN0YXR1cyBhbmQgU2F2aW5ncyBBbW91bnQiLA0KICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJTYXZpbmdzIEFtb3VudCIpLA0KICAgIHlheGlzID0gbGlzdCgNCiAgICAgIHRpdGxlID0gIk1hcml0YWwgU3RhdHVzIiwgDQogICAgICBzaG93dGlja2xhYmVscyA9IEZBTFNFLCANCiAgICAgIGNhdGVnb3J5YXJyYXkgPSBsaXN0KCJVbmtub3duIiwgIlNpbmdsZSIsICJNYXJyaWVkIiksDQogICAgICBjYXRlZ29yeW9yZGVyID0gImFycmF5Ig0KICAgICksDQogICAgbGVnZW5kID0gbGlzdCgNCiAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBNYXJpdGFsIFN0YXR1cyA8L2I+IiksDQogICAgICBiZ2NvbG9yID0gIiNFMkUyRTIiLA0KICAgICAgYm9yZGVyY29sb3IgPSAiI0ZGRkZGRiIsDQogICAgICBib3JkZXJ3aWR0aCA9IDINCiAgICApDQogICkNCg0KIyBPdXRwdXR0aW5nIHBsb3QNCkZlYXRSZWwwMw0KYGBgDQoNCg0KIyMgYENoZWNraW5nX2Ftb3VudGAsIGBDcmVkaXRfc2NvcmVgLCBhbmQgYFNhdmluZ19hbW91bnRgDQoNClRoZXJlIGRvIG5vdCBhcHBlYXIgdG8gYmUgbW9yZSB0aGFuIG9uZSBjbHVzdGVyIG9mIHBvaW50cyBvciBhbnkgb3RoZXIgZm9ybSBvZiBjbGVhciBkaXZpZGUgdGhhdCB3b3VsZCBpbmRpY2F0ZSBhZGRpdGlvbmFsIGNhdGVnb3JpZXMgZm9yIHVzIHRvIGRpc2NvdmVyIGZyb20gdGhpcyBncmFwaC4NCg0KYGBge3J9DQojID09PT09PT09PT09PT09PT09PT09DQojIFBMT1RUSU5HIGBDaGVja2luZ19hbW91bnRgLCBgQ3JlZGl0X3Njb3JlYCwgQU5EIGBTYXZpbmdfYW1vdW50YA0KIyA9PT09PT09PT09PT09PT09PT09PQ0KDQojIFByZXBhcmluZyBwbG90IGRhdGENCkNoZWNBbW91X0NyZWRTY29fU2F2QW1vdSA8LSANCiAgIyBTdWJzZXR0aW5nIGxvYW5fZGVmYXVsdF9Nb2QwMSB0byBqdXN0IENoZWNraW5nX2Ftb3VudCwgQ3JlZGl0X3Njb3JlLCBhbmQgU2F2aW5nX2Ftb3VudA0KICBsb2FuX2RlZmF1bHRfTW9kMDFbICwgYygiQ2hlY2tpbmdfYW1vdW50IiwgIkNyZWRpdF9zY29yZSIsICJTYXZpbmdfYW1vdW50IildDQoNCiMgR2VuZXJhdGluZyBwbG90DQpGZWF0UmVsMDQgPC0gcGxvdF9seSgpICU+JSANCiAgYWRkX3RyYWNlKA0KICAgIGRhdGEgPSBDaGVjQW1vdV9DcmVkU2NvX1NhdkFtb3UsDQogICAgeCA9IH5DaGVja2luZ19hbW91bnQsIA0KICAgIHkgPSB+Q3JlZGl0X3Njb3JlLCANCiAgICB6ID0gflNhdmluZ19hbW91bnQsDQogICAgbWFya2VyID0gbGlzdChzaXplID0gMiksDQogICAgaG92ZXJ0ZW1wbGF0ZSA9IHBhc3RlKA0KICAgICAgIjxiPkNoZWNraW5nIEFtdDwvYj46ICV7eH08YnI+IiwNCiAgICAgICI8Yj5DcmVkaXQgU2NvcmU8L2I+OiAle3l9PGJyPiIsDQogICAgICAiPGI+U2F2aW5nIEFtdDwvYj46ICV7en0iDQogICAgKSwNCiAgICBuYW1lID0gIiINCiAgKSAlPiUNCiAgbGF5b3V0KA0KICAgIHRpdGxlID0gIkNoZWNraW5nIEFtb3VudCwgQ3JlZGl0IFNjb3JlLCBhbmQgU2F2aW5ncyBBbW91bnQiLA0KICAgIHNjZW5lID0gbGlzdCgNCiAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICJDaGVja2luZyBBbW91bnQiKSwNCiAgICAgIHlheGlzID0gbGlzdCh0aXRsZSA9ICJDcmVkaXQgU2NvcmUiKSwNCiAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJTYXZpbmdzIEFtb3VudCIpLA0KICAgICAgYXNwZWN0bW9kZSA9ICJjdWJlIg0KICAgICkNCiAgKQ0KDQojIE91dHB1dHRpbmcgcGxvdA0KRmVhdFJlbDA0DQpgYGANCg0K